package com.matrix.draft.sockettest.day3;

import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.stream.IntStream;

public class FileChannelTest {

    public static void main(String[] args) {
        serverSocketChannel();
    }

    public static void write() {
        try (FileOutputStream fos = new FileOutputStream("D:\\res\\temp\\fileChannel.txt");) {
            // 获取Channel
            FileChannel fileChannel = fos.getChannel();
            // 准备Buffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            byteBuffer.put("Hello, FileChannel管道Hello, FileChannel管道Hello, FileChannel管道".getBytes(StandardCharsets.UTF_8));
            // 一定要调用flip归位, 在进行其他操作
            byteBuffer.flip();
            // 写入管道
            fileChannel.write(byteBuffer);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void read() {
        FileInputStream fis = null;
        try {
            File f = new File("D:\\res\\temp\\fileChannel.txt");
            fis = new FileInputStream(f);
            FileChannel channel = fis.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate((int) f.length());
            channel.read(buffer);
            buffer.flip();
            System.out.println(new String(buffer.array(), 0, (int) f.length(), StandardCharsets.UTF_8));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void copy() {
        try (
                FileInputStream fis = new FileInputStream("D:\\res\\temp\\fileChannel.txt");
                FileOutputStream fos = new FileOutputStream("D:\\res\\temp\\fileChannel2.txt");
        ) {
            // 准备源文件管道, 和目标文件的管道
            FileChannel inChannel = fis.getChannel();
            FileChannel outChannel = fos.getChannel();
            // 准备Buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            // 边读边写(一次读1024字节)
            int len = -1;
            while ((len = inChannel.read(buffer)) != -1) {
                // 由于上面的read移动了position, 所以position需要归零, 方便write从0开始操作
                buffer.flip();
                // 写
                outChannel.write(buffer);
                // 由于上面的write也移动了position, 所以position需要再次归零, 方便read从0开始操作
                buffer.clear(); // 这里也可以用flip()
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void quickCopy() {
        try (
                FileInputStream fis = new FileInputStream("D:\\res\\temp\\money.jpg");
                FileOutputStream fos = new FileOutputStream("D:\\res\\temp\\money2.jpg");
                FileChannel inChannel = fis.getChannel();
                FileChannel outChannel = fos.getChannel();
        ) {
            // 直接调用转移方法
            outChannel.transferFrom(inChannel, 0, inChannel.size());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void sortedBuffer() {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 如果按照下面的顺序写
        buffer.putInt(1);
        buffer.putShort((short) 2);
        buffer.putLong(3L);
        buffer.putDouble(4.0);
        buffer.putChar('Q');
        // flip
        buffer.flip();
        // 必须按照下面的顺序读
        System.out.println(buffer.getInt());
        System.out.println(buffer.getShort());
        System.out.println(buffer.getLong());
        System.out.println(buffer.getDouble());
        System.out.println(buffer.getChar());
    }

    public static void readOnlyBuffer() {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        IntStream.range(0, 10).forEach(buffer::putInt);
        buffer.flip();
        ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
        while (readOnlyBuffer.hasRemaining()) {
            System.out.println(readOnlyBuffer.getInt());
        }
    }

    public static void mappedByteBuffer() {
        try (
                // MappedByteBuffer可以让文件直接在内存(堆外内存)修改, 操作系统不需要拷贝一次
                RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\res\\temp\\access.txt", "rw");
        ) {
            // 通道
            FileChannel channel = randomAccessFile.getChannel();
            // MappedByteBuffer (参数分别为: 模式,起始位置,要映射的字节大小)
            MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
            mappedByteBuffer.put(0, (byte) 'H');
            mappedByteBuffer.put(3, (byte) 'W');
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void serverSocketChannel() {
        try {
            // 启动服务
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            while (true) {
                // 等待请求
                SocketChannel socketChannel = serverSocketChannel.accept();
                // 创建Buffer数组
                ByteBuffer[] buffers = {
                        ByteBuffer.allocate(5),
                        ByteBuffer.allocate(3)
                };
                // 读
                long len = -1;
                while ((len = socketChannel.read(buffers)) != -1) {
                    // 分批读
                    Arrays.asList(buffers).stream()
                            .map(b -> "Buffer{position=" + b.position() + ",limit=" + b.limit() + "}")
                            .forEach(System.out::println);
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
